const DEFAULT_CLIENT_INFO = {
    name: 'pg-zdm-extension',
    version: '1.0.0'
};

const DEFAULT_CAPABILITIES = {};
const DEFAULT_PROTOCOL_VERSION = '2025-03-26';

const truncateForLog = (value, max = 1000) => {
    if (value === undefined || value === null) {
        return value;
    }
    if (typeof value !== 'string') {
        try {
            value = JSON.stringify(value);
        } catch (error) {
            value = String(value);
        }
    }
    return value;
    // return value.length > max ? `${value.slice(0, max)}…` : value;
};


class MCPError extends Error {
    constructor(message, code, data) {
        super(message);
        this.name = 'MCPError';
        this.code = code;
        this.data = data;
    }
}

export class MCPClient {
    constructor(config = {}) {
        this.baseUrl = (config.url || 'http://127.0.0.1:12306/mcp').replace(/\/$/, '');
        this.timeout = config.timeout || 30000;
        this.clientInfo = config.clientInfo || DEFAULT_CLIENT_INFO;
        this.clientCapabilities = config.capabilities || DEFAULT_CAPABILITIES;
        this.preferredProtocolVersion = config.protocolVersion || DEFAULT_PROTOCOL_VERSION;

        this.requestId = 0;
        this.tools = new Map();
        this.initialized = false;
        this.initializingPromise = null;

        this.sessionId = null;
        this.protocolVersion = this.preferredProtocolVersion;
        this.serverInfo = null;
        this.lastEventId = null;
    }

    async initialize() {
        if (this.initialized) {
            return;
        }

        if (this.initializingPromise) {
            return this.initializingPromise;
        }

        console.log(`[MCP] 正在连接到: ${this.baseUrl}`);

        this.initializingPromise = (async () => {
            try {
                console.log('[MCP] 发送初始化请求...');
                const initResponse = await this.sendJsonRpc('initialize', {
                    clientInfo: this.clientInfo,
                    capabilities: this.clientCapabilities,
                    protocolVersion: this.protocolVersion || this.preferredProtocolVersion
                });

                const initResult = initResponse.result || {};
                this.protocolVersion = initResult.protocolVersion || this.protocolVersion;
                this.serverInfo = initResult.serverInfo || null;

                console.log('[MCP] 初始化响应成功，开始加载工具...');
                await this.loadToolsFromServer();

                this.initialized = true;
                console.log('[MCP] 初始化成功（JSON-RPC模式）');
            } catch (error) {
                console.error('[MCP] 初始化失败:', error);
                this.initialized = false;
                throw new Error(`MCP 服务连接失败: ${error.message}`);
            } finally {
                this.initializingPromise = null;
            }
        })();

        return this.initializingPromise;
    }

    async loadToolsFromServer() {
        try {
            console.log('[MCP] 请求工具列表...');
            const response = await this.sendJsonRpc('tools/list');
            const listResult = response.result || response;
            this.parseToolsResponse(listResult);
            console.log('[MCP] 工具列表加载完成');
        } catch (error) {
            console.error('[MCP] 加载工具列表失败:', error);
            // 清空工具列表
            this.tools.clear();
            throw error;
        }
    }

    async loadTools() {
        if (!this.initialized) {
            await this.initialize();
        }
        await this.loadToolsFromServer();
    }

    parseToolsResponse(toolsResponse) {
        const tools = this.extractToolsArray(toolsResponse);

        if (!tools || !Array.isArray(tools)) {
            console.warn('[MCP] 未找到工具列表，响应格式:', toolsResponse);
            return;
        }

        this.tools.clear();
        for (const tool of tools) {
            this.tools.set(tool.name, tool);
        }
        console.log(`[MCP] 已加载 ${this.tools.size} 个工具:`, Array.from(this.tools.keys()));
    }

    extractToolsArray(payload) {
        if (!payload) {
            return null;
        }

        if (Array.isArray(payload)) {
            return payload;
        }

        if (Array.isArray(payload.tools)) {
            return payload.tools;
        }

        if (payload.result && Array.isArray(payload.result.tools)) {
            return payload.result.tools;
        }

        if (payload.data && Array.isArray(payload.data)) {
            return payload.data;
        }

        return null;
    }

    async callTool(toolName, arguments_ = {}) {
        if (!this.initialized) {
            await this.initialize();
        }

        if (!this.tools.has(toolName)) {
            throw new Error(`工具 "${toolName}" 不存在。可用工具: ${Array.from(this.tools.keys()).join(', ')}`);
        }

        const request = () => this.sendJsonRpc('tools/call', {
            name: toolName,
            arguments: arguments_
        });

        try {
            const response = await request();
            return response.result ?? response;
        } catch (error) {
            if (this.shouldResetSession(error)) {
                console.warn('[MCP] 会话失效，正在重新初始化...');
                this.resetSession();
                await this.initialize();
                const retryResponse = await request();
                return retryResponse.result ?? retryResponse;
            }
            console.error(`[MCP] 调用工具 "${toolName}" 失败:`, error);
            throw new Error(`工具调用失败: ${error.message}`);
        }
    }

    shouldResetSession(error) {
        if (!(error instanceof MCPError)) {
            return false;
        }
        if (error.code === -32001) {
            return true; // Session not found
        }
        if (typeof error.message === 'string') {
            return error.message.includes('Mcp-Session-Id') || error.message.includes('Server not initialized');
        }
        return false;
    }

    resetSession() {
        this.sessionId = null;
        this.protocolVersion = this.preferredProtocolVersion;
        this.initialized = false;
        this.lastEventId = null;
    }

    async sendJsonRpc(method, params = {}) {
        const id = `req-${++this.requestId}`;
        const message = {
            jsonrpc: '2.0',
            id,
            method,
            params
        };

        const headers = await this.buildHeaders();
        const payloadText = JSON.stringify(message);
        console.log('[MCP][request]', {
            id,
            method,
            hasSession: Boolean(this.sessionId),
            headers,
            bodyPreview: truncateForLog(payloadText, 600)
        });

        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.timeout);

        try {
            const response = await fetch(this.baseUrl, {
                method: 'POST',
                headers,
                body: payloadText,
                signal: controller.signal
            });

            clearTimeout(timeoutId);
            this.captureSessionHeaders(response.headers);

            const contentType = response.headers.get('content-type') || '';
            console.log('[MCP][response:headers]', {
                id,
                status: response.status,
                statusText: response.statusText,
                contentType,
                sessionId: this.sessionId
            });

            if (!response.ok && response.status !== 202) {
                const errorText = await this.safeReadText(response);
                console.log('[MCP][response:error-body]', truncateForLog(errorText, 800));
                throw new Error(`HTTP ${response.status}: ${errorText || response.statusText}`);
            }

            if (contentType.includes('application/json')) {
                const rawText = await this.safeReadText(response) || '';
                console.log('[MCP][response:body]', truncateForLog(rawText, 800));
                let payload = {};
                if (rawText) {
                    try {
                        payload = JSON.parse(rawText);
                    } catch (parseError) {
                        console.log('[MCP][response:json-error]', parseError);
                        throw new Error(`无法解析 JSON 响应: ${rawText}`);
                    }
                }
                const normalized = this.normalizeRpcResponse(payload, id);
                console.log('[MCP][response:parsed]', normalized);
                return normalized;
            }

            if (contentType.includes('text/event-stream')) {
                console.log('[MCP][response] 检测到 SSE 流，开始读取');
                const stream = response.body;
                if (!stream) {
                    throw new Error('响应流为空');
                }
                const message = await this.readSseResponse(stream, id);
                console.log('[MCP][response:parsed]', message);
                return message;
            }

            if (response.status === 202) {
                console.warn('[MCP][response] 收到 202，当前客户端未实现该模式');
                throw new Error('服务器返回 202，当前客户端尚未处理该模式');
            }

            const fallbackText = await this.safeReadText(response);
            console.log('[MCP][response:unknown]', fallbackText);
            throw new Error(`未知响应格式: ${fallbackText || contentType || response.status}`);
        } catch (error) {
            clearTimeout(timeoutId);
            if (error.name === 'AbortError') {
                console.error('[MCP][response] 请求超时', { id, method });
                throw new Error('MCP 请求超时');
            }
            console.error('[MCP][response] 请求失败', { id, method, error: truncateForLog(error?.message || error, 800) });
            throw error;
        }
    }

    async buildHeaders() {
        const headers = {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/event-stream'
        };

        if (this.sessionId) {
            headers['Mcp-Session-Id'] = this.sessionId;
        }

        if (this.protocolVersion) {
            headers['Mcp-Protocol-Version'] = this.protocolVersion;
        }

        return headers;
    }

    captureSessionHeaders(headers) {
        const sessionHeader = headers.get('mcp-session-id');
        if (sessionHeader) {
            this.sessionId = sessionHeader;
        }
        const protocolHeader = headers.get('mcp-protocol-version');
        if (protocolHeader) {
            this.protocolVersion = protocolHeader;
        }
    }

    normalizeRpcResponse(payload, expectedId) {
        const message = Array.isArray(payload) ? payload[0] : payload;

        if (!message) {
            throw new Error('MCP 响应为空');
        }

        if (message.id && message.id !== expectedId) {
            console.warn(`[MCP] 收到意外的响应 ID: ${message.id} (期待 ${expectedId})`);
        }

        if (message.error) {
            throw new MCPError(message.error.message || '未知错误', message.error.code, message.error.data);
        }

        return message;
    }

    async readSseResponse(stream, expectedId) {
        const decoder = new TextDecoder();
        const reader = stream.getReader();
        let buffer = '';

        return new Promise((resolve, reject) => {
            const timeoutId = setTimeout(() => {
                reader.cancel().catch(() => {});
                reject(new Error('MCP 响应超时'));
            }, this.timeout);

            const handleMessage = (raw) => {
                const message = this.parseSseEvent(raw);
                if (!message) {
                    return;
                }

                if (message.id && message.id !== expectedId) {
                    console.log('[MCP] 收到非目标ID的消息:', message);
                    return;
                }

                console.log('[MCP][SSE] 收到消息', truncateForLog(message, 800));

                clearTimeout(timeoutId);
                reader.cancel().catch(() => {});

                if (message.error) {
                    reject(new MCPError(message.error.message || '未知错误', message.error.code, message.error.data));
                    return;
                }

                resolve(message);
            };

            const pull = () => {
                reader.read().then(({ value, done }) => {
                    if (done) {
                        clearTimeout(timeoutId);
                        reject(new Error('SSE 流提前结束'));
                        return;
                    }

                    buffer += decoder.decode(value, { stream: true });

                    let separatorIndex;
                    while ((separatorIndex = this.findSseSeparator(buffer)) !== -1) {
                        const rawEvent = buffer.slice(0, separatorIndex);
                        buffer = buffer.slice(separatorIndex + this.separatorLength(buffer, separatorIndex));
                        try {
                            handleMessage(rawEvent);
                        } catch (error) {
                            clearTimeout(timeoutId);
                            reject(error);
                            return;
                        }
                    }

                    pull();
                }).catch(error => {
                    clearTimeout(timeoutId);
                    reject(error instanceof Error ? error : new Error(String(error)));
                });
            };

            pull();
        });
    }

    findSseSeparator(buffer) {
        const lfIndex = buffer.indexOf('\n\n');
        const crlfIndex = buffer.indexOf('\r\n\r\n');

        if (lfIndex === -1) {
            return crlfIndex;
        }
        if (crlfIndex === -1) {
            return lfIndex;
        }
        return Math.min(lfIndex, crlfIndex);
    }

    separatorLength(buffer, index) {
        if (buffer.slice(index, index + 4) === '\r\n\r\n') {
            return 4;
        }
        return 2;
    }

    parseSseEvent(rawEvent) {
        const cleaned = rawEvent.replace(/\r/g, '');
        const lines = cleaned.split('\n');
        const dataLines = [];
        let eventId = undefined;

        for (const line of lines) {
            if (!line) {
                continue;
            }
            if (line.startsWith(':')) {
                continue; // comment / keep-alive
            }
            if (line.startsWith('id:')) {
                eventId = line.slice(3).trim();
                this.lastEventId = eventId;
                continue;
            }
            if (line.startsWith('data:')) {
                dataLines.push(line.slice(5).trimStart());
            }
        }

        if (dataLines.length === 0) {
            return null;
        }

        const data = dataLines.join('\n');

        try {
            const message = JSON.parse(data);
            if (eventId && !message.id) {
                message.id = eventId;
            }
            return message;
        } catch (error) {
            console.warn('[MCP] 无法解析 SSE 数据:', data, error);
            return null;
        }
    }

    async safeReadText(response) {
        try {
            return await response.text();
        } catch (error) {
            console.warn('[MCP] 响应正文读取失败:', error);
            return null;
        }
    }

    getAvailableTools() {
        return Array.from(this.tools.keys());
    }

    getToolSchema(toolName) {
        return this.tools.get(toolName);
    }

    getToolsDescription() {
        const toolsDesc = [];
        for (const [name, tool] of this.tools) {
            toolsDesc.push({
                name: tool.name,
                description: tool.description,
                inputSchema: tool.inputSchema
            });
        }
        return toolsDesc;
    }

    generateToolsPrompt() {
        if (this.tools.size === 0) {
            return '当前没有可用的 MCP 工具。';
        }

        const sections = [];

        for (const [, tool] of this.tools) {
            const lines = [`### ${tool.name}`];

            if (tool.description) {
                lines.push(`- 描述: ${tool.description}`);
            }

            const schema = tool.inputSchema || {};
            const properties = schema.properties || {};
            const required = new Set(schema.required || []);
            const entries = Object.entries(properties);

            if (entries.length) {
                lines.push('- 参数:');
                for (const [paramName, paramSchema] of entries) {
                    const type = Array.isArray(paramSchema?.type)
                        ? paramSchema.type.join(' | ')
                        : (paramSchema?.type || 'unknown');
                    const desc = paramSchema?.description || '无描述';
                    const necessity = required.has(paramName) ? '必填' : '可选';
                    lines.push(`  - ${paramName} (${type}, ${necessity}): ${desc}`);
                }
            } else {
                lines.push('- 参数: 无');
            }

            sections.push(lines.join('\n'));
        }

        return sections.join('\n\n');

    }

    cleanup() {
        this.sessionId = null;
        this.initialized = false;
        this.initializingPromise = null;
        this.lastEventId = null;
        console.log('[MCP] 已清理连接资源');
    }
}

const mcpClientInstances = new Map();

export function getMCPClient(config = {}) {
    const normalizedUrl = (config.url || 'http://127.0.0.1:12306/mcp').replace(/\/$/, '');

    if (!mcpClientInstances.has(normalizedUrl)) {
        mcpClientInstances.set(normalizedUrl, new MCPClient({ ...config, url: normalizedUrl }));
    }

    const client = mcpClientInstances.get(normalizedUrl);

    if (config.timeout && client.timeout !== config.timeout) {
        client.timeout = config.timeout;
    }

    return client;
}
